using System;
using System.Globalization;
using System.IO;

using Atomic.Vasp.Options;

namespace Atomic.Vasp.LinearResponse
{
	/// <summary>
	/// Finite difference scheme (POTIM and NFREE) for linear response problems. Only relevant if finite difference is enabled.
	/// http://cms.mpi.univie.ac.at/vasp/vasp/IBRION_5_IBRION_6.html
	/// </summary>
	[Serializable]
	public class LinearResponseFiniteDifference : VaspLinearResponseOption
	{
		public LinearResponseFiniteDifference(double stepSize)
			: this(stepSize, null)
		{
		}
	
		public LinearResponseFiniteDifference(double stepSize, int? points)
			: this(true, stepSize, points)
		{
		}

		public LinearResponseFiniteDifference(bool useSymmetry, double stepSize)
			: this(useSymmetry, stepSize, null)
		{
		}
	
		public LinearResponseFiniteDifference(bool useSymmetry, double stepSize, int? points)
		{
			if (stepSize < 0.0)
			{
				throw new ArgumentException("Invalid step size.");
			}

			if (points.HasValue && points.Value != 1 && points.Value != 2 && points.Value != 4)
			{
				throw new ArgumentException("Invalid number of finite difference points.");
			}

			StepSize = stepSize;
			Points = points;
		}

		public override void Write(VaspIncar incar, DirectoryInfo directory)
		{
			// http://cms.mpi.univie.ac.at/vasp/vasp/IBRION_5_IBRION_6.html
			int code;
			if (UseSymmetry)
			{
				code = 6;
			}
			else
			{
				code = 5;
			}

			incar.Add("IBRION", code.ToString(), "Linear response");

			// Finite difference step size.
			incar.Add("POTIM", StepSize.ToString("r", CultureInfo.InvariantCulture));

			// Number of points used to determine finite difference derivative.
			if (Points.HasValue)
			{
				incar.Add("NFREE", Points.Value.ToString());
			}
		}
		
		public bool UseSymmetry
		{
			get;
			private set;
		}

		public double StepSize
		{
			get;
			private set;
		}

		public int? Points
		{
			get;
			private set;
		}
	}
}
